/**
 * Copyright (C) 2009-2012, KingHool.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses.
 */
package com.kinghool.smodel.io.bean.annotation;

import java.lang.reflect.Field;

import com.kinghool.ebos.commons.utils.Assert;
import com.kinghool.ebos.commons.utils.Utils;
import com.kinghool.smodel.io.bean.BaseBeanChannelProvider;
import com.kinghool.smodel.io.bean.IBeanChannelProvider;
import com.kinghool.smodel.io.bean.xml.ArrayElementBeanFieldModel;
import com.kinghool.smodel.io.bean.xml.AttributeBeanFieldModel;
import com.kinghool.smodel.io.bean.xml.AutoBeanFieldModel;
import com.kinghool.smodel.io.bean.xml.BeanFieldModel;
import com.kinghool.smodel.io.bean.xml.BeanModel;
import com.kinghool.smodel.io.bean.xml.ChildBeanFieldModel;
import com.kinghool.smodel.io.bean.xml.TextBeanFieldModel;

/**
 * 
 * @Author Allen Deng(dengtailin@gmail.com) 
 */
public class AnnotationBeanChannelProvider extends BaseBeanChannelProvider implements IBeanChannelProvider {

	public AnnotationBeanChannelProvider(Class<?> beanClass) {
		super(beanClass);
	}

	public BeanModel getBeanModel(Class<?> beanClass) {
		BeanModel beanModel = new BeanModel();
		beanModel.setClassName(beanClass.getName());
		
		boolean includeAll = true;
		
		if (beanClass.isAnnotationPresent(Bean.class)) {
			Bean beanAnnotation = beanClass.getAnnotation(Bean.class);
			includeAll = beanAnnotation.includeAll();
			beanModel.setIncludeAll(includeAll);
			
			beanModel.setName(beanAnnotation.name());
		}
		
		for (Field field : Utils.getDeclaredFieldsRecursively(beanClass)) {
			if (field.isAnnotationPresent(Exclude.class)
				|| (!includeAll && !field.isAnnotationPresent(Include.class))) {
			    continue;
			}
			
			BeanFieldModel bfModel = null;			
			if (field.isAnnotationPresent(Attribute.class)) {
				Attribute attribute = field.getAnnotation(Attribute.class);
				String name = attribute.name();
				if (Assert.isNullOrEmpty(name)) {
					name = field.getName();
				}
				String child = attribute.child();
				bfModel = new AttributeBeanFieldModel();
				((AttributeBeanFieldModel)bfModel).setChild(child);
				((AttributeBeanFieldModel)bfModel).setName(name);				
				
			} else if (field.isAnnotationPresent(Text.class)) {
				Text txt = field.getAnnotation(Text.class);
				String child = txt.child();

				bfModel = new TextBeanFieldModel();
				((TextBeanFieldModel)bfModel).setChild(child);	
			} else if (field.isAnnotationPresent(Child.class)) {
				Child element = field.getAnnotation(Child.class);
				String child = element.child();
				
				if (Assert.isNullOrEmpty(child)) {
					child = field.getName();
				}

				bfModel = new ChildBeanFieldModel();
				((ChildBeanFieldModel)bfModel).setChild(child);
				((ChildBeanFieldModel)bfModel).setChildClassName(field.getType().getName());
				((ChildBeanFieldModel)bfModel).setChildBeanModel(getBeanModel(field.getType()));				
			} else if (field.isAnnotationPresent(Array.class)) {
				if (!Assert.isArray(field.getType())) {
					throw new IllegalArgumentException("Annotation of " + Array.class.getName() + " should be annotated on array object, such as[List, Set, Object[]].");
				}
				
				Array arrayElement = field.getAnnotation(Array.class);

				String container = arrayElement.container();
				String child = arrayElement.child();
				String get = arrayElement.get();
				String set = arrayElement.set();
				String add = arrayElement.add();
				String clear = arrayElement.clear();

				if (Assert.isNullOrEmpty(get)) {
					get = getDefaultGetArrayMethodName(field.getName());
				}
				if (Assert.isNullOrEmpty(set)) {
					set = getDefaultSetArrayMethodName(field.getName());
				}
				if (Assert.isNullOrEmpty(child)) {
					child = getDefaultArrayChildFullName(field.getName());
				}
				
				Class<?> fieldClazz = getArrayFieldType(field);

				bfModel = new ArrayElementBeanFieldModel();
				((ArrayElementBeanFieldModel)bfModel).setContainer(container);
				((ArrayElementBeanFieldModel)bfModel).setAdd(add);
				((ArrayElementBeanFieldModel)bfModel).setClear(clear);
				((ArrayElementBeanFieldModel)bfModel).setGet(get);
				((ArrayElementBeanFieldModel)bfModel).setSet(set);
				((ArrayElementBeanFieldModel)bfModel).setChild(child);
				
				boolean isText = isTextObject(fieldClazz);
				if (!isText) {
					((ArrayElementBeanFieldModel)bfModel).setChildClassName(fieldClazz.getName());
					((ArrayElementBeanFieldModel)bfModel).setChildBeanModel(getBeanModel(fieldClazz));	
				}
			} else {
				bfModel = new AutoBeanFieldModel();
			}
			
			//set common properties of BeanFieldModel
			bfModel.setFieldName(field.getName());			
            if (field.isAnnotationPresent(Exclude.class)) {
            	bfModel.setExcludeExist(true);
			}			
			if (field.isAnnotationPresent(Include.class)) {
				bfModel.setIncludeExist(true);
			}
			
			beanModel.addBeanField(bfModel);
		}
		
		return beanModel;
	}

	protected BeanModel getAutoChildBeanModel(Class<?> clazz) {		
		return getBeanModel(clazz);
	}
 
}
